下面的c语言程序示例展示了函数, 参数, 本地变量和局部变量(函数简短注释)

/* An example C program with local and global variables */ 
#include <stdio.h> 

int max(int n1, int n2); /* function prototypes */ 
int change(int amt); 

int g_x; /* global variable: declared outside function bodies */ 

int main(void) { 
	int x, result; /* local variables: declared inside function bodies */ 
	
	printf("Enter a value: "); 
	scanf("%d", &x); 
	g_x = 10; /* global variables can be accessed in any function */ 
	
	result = max(g_x, x); 
	printf("%d is the largest of %d and %d\n", result, g_x, x); 
	
	result = change(10); 
	printf("g_x's value was %d and now is %d\n", result, g_x); 
	
	return 0; 

} 

int max(int n1, int n2) { /* function with two parameters */ 
	int val; /* local variable */ 
	
	val = n1; 
	if ( n2 > n1 ) { 
		val = n2; 
	} 
	return val; 
} 

int change(int amt) { 
	int val; 
	
	val = g_x; /* global variables can be accessed in any function */ 
	g_x += amt; 
	return val; 
}

这个例子展示了程序变量的不同作用范围. 变量的作用范围由它们的定义决定.换句话说, 作用范围是由代码块中的变量及其关联使用的程序内存决定.

在函数外面定义的变量是全局变量. 全局变量永久有效, 在程序的任何地方都可以被访问, 因为它们被放在特别的内存区域. 每一个全局变量必须拥有唯一的名字 ---- 在整个程序运行期间这个唯一的名字代表对一个指定存储的标识符.

局部变量和参数的作用域在函数定义的范围内. 举个例子, amt 参数的作用域在 change 函数内. 这意味着只有 change 函数内的语句才能使用 amt 参数, 并且 amt 参数也会在随着每一个 change 函数实例执行时(当函数被被调用时, 会在栈上分配空间)分配一块内存. 当函数被调用执行时, 在栈上分配参数值的空间, 当函数返回后, 参数值的空间被释放. 函数的每一次调用都会给自己参数和局部变量分配空间. 因此, 对于递归函数的运行, 每一次递归调用都会在栈上申请包含参数和局部变量的函数空间.

因为参数和局部变量的作用域在函数定义的内部, 不同的函数可以使用相同名字来代表局部变量和函数参数. 举个例子, changemax 函数有一样名字的局部变量 val. max 函数中的 val 变量不会改变 change 函数内部的局部变量 val, 它们的作用域都在函数内.

虽然有时可能会使用c语言的全局变量, 我们强烈建议尽可能避免使用全局变量. 使用局部变量和参数可以让代码更加模块化, 更通用, 更易于调试. 同时, 因为在函数调用时分配函数参数和局部变量空间, 这种按需分配可以让程序空间利用更加高效.

当启动一个新的程序, 操作系统会分配新的程序地址空间. 一个程序的地址空间(内存空间)代表着执行过程中所有内容存储位置, 包括存储所有的指令和数据. 程序空间可以由一系列的可寻址的字节数组组成; 程序空间中的每个使用的地址存储全部和部分的指令和数据(一些程序执行时需要的附加状态).

程序内存空间会分成几个部分, 每个部分用于在进程的地址空间中存储不同类型的代码状态实体. Figure 1对程序内存空间的各个组成部分做了说明. The parts of program memory arranged into a program’s address space.  At the top (addresses closer to 0), we have regions for the OS, code (instructions), data (globals), and the heap (dynamically allocated memory).  At the other end of the address space (maximum address), the stack stores local variables and function parameters.

Figure 1. 程序内存地址空间布局

程序内存布局的顶部(内存地址从上到下增长)保留给操作系统使用, 剩下的部分给程序使用. 程序的  code (代码段)用来存储指令. 举个例子, 上面示例程序代码段存储 main, max, change 这些函数指令.

局部变量和参数驻留在 stack(栈: 先进后出的数据存储容器)中. 随着函数的调用和返回, 堆栈空间的大小随着程序的执行而增长和收缩. 内存的堆栈的新增部分通常分配在内存底部附近(从高内存地址往低内存地址增长), 这可以为栈的改变留出空间. 当函数被调用(函数被调用时会在栈上分配对应的栈帧(stack frame))时, 局部变量和参数才会在栈上分配空间.

全局变量存储在_data_(数据段). 不像栈那样, 数据段不会增长会缩小 — 全局变量的存储空间在程序的整个运行过程中持续存在.

最后, heap (堆空间)是程序地址空间中与动态内存分配相关的部分. 堆空间一般远离栈空间, 并且堆空间随着程序的运行会进行更多的动态内存分配, 增长方向是从低内存地址往高内存地址方向增长. 这和栈空间增长方向相反.